/*
 * Copyright (C) 2019 xuexiangjys(xuexiangjys@163.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.xuexiang.xui_lib.tabbar;

import ohos.agp.colors.RgbColor;
import ohos.agp.components.AbsButton;
import ohos.agp.components.AttrSet;
import ohos.agp.components.ComponentContainer;
import ohos.agp.components.ComponentState;
import ohos.agp.components.RadioButton;
import ohos.agp.components.RadioContainer;
import ohos.agp.components.element.ShapeElement;
import ohos.agp.components.element.StateElement;
import ohos.agp.text.Font;
import ohos.agp.utils.Color;
import ohos.agp.utils.TextAlignment;
import ohos.app.Context;
import ohos.global.configuration.Configuration;

import com.xuexiang.xui_lib.ResourceTable;
import com.xuexiang.xui_lib.component.textview.supertextview.adapter.HasTypeface;
import com.xuexiang.xui_lib.util.AttrValue;
import com.xuexiang.xui_lib.util.DisplayUtils;
import com.xuexiang.xui_lib.util.TypedAttrUtils;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;

/**
 * 选项卡控制器（单选）（RadioButton实现）
 *
 * @author xuexiang
 * @since 2019/1/14 下午10:03
 */
public class TabControlView extends RadioContainer implements HasTypeface {
    private static final int VALUE_TEN = 10;
    private static final int VALUE_TWENTY = 20;
    private static final int DEFAULT_SELECT = -1;
    private static final int mDefaultTextSize = 14;
    private static final int mDefaultItemPadding = -1;
    private static final int mPadding = 10;
    private static final int mPaddingTopBottom = 8;
    private static final int mDefaultRadius = 5;
    private Context mContext;

    /**
     * Tab选中的监听
     */
    private OnTabSelectionChangedListener mListener;

    /**
     * 字体大小
     */
    private int mTextSize;
    /**
     * 边框宽度
     */
    private int mStrokeWidth;
    /**
     * 选项间距
     */
    private int mItemPadding;
    /**
     * 选项水平间距
     */
    private int mItemPaddingHorizontal;
    /**
     * 选项垂直间距
     */
    private int mItemPaddingVertical;
    /**
     * 选中背景的颜色
     */
    private int mSelectedColor;
    /**
     * 未选中背景的颜色
     */
    private int mUnselectedColor;
    /**
     * 选中文字的颜色
     */
    private int mSelectedTextColor;
    /**
     * 未选中文字的颜色
     */
    private int mUnselectedTextColor;
    /**
     * 默认选中项的索引
     */
    private int mDefaultSelection = DEFAULT_SELECT;
    /**
     * 是否展开（填充满）
     */
    private boolean mStretch = false;
    /**
     * 是否等宽显示
     */
    private boolean mEqualWidth = false;
    private StateElement mTextColorStateList;

    // Item organization

    private LinkedHashMap<String, String> mItemMap = new LinkedHashMap<>();
    private List<RadioButton> mOptions;
    /**
     * Used to pass along the selection change event
     * Calls onSelectionChangedListener with mIdentifier and value of selected segment
     */
    private CheckedStateChangedListener mSelectionChangedListener = new CheckedStateChangedListener() {
        @Override
        public void onCheckedChanged(RadioContainer radioContainer, int checkedId) {
            if (mListener != null) {
                String identifier = ((RadioButton) radioContainer.getComponentAt(checkedId)).getText();
                String value = mItemMap.get(identifier);
                mListener.newSelection(identifier, value);
            }
        }
    };

    private AbsButton.CheckedStateChangedListener mCheckedStateChangedListener = (absButton, b) -> {
        absButton.setClickable(!b);
    };

    public TabControlView(Context context) {
        super(context, null);

        // Initialize
        init(context);

        // Setup the view
        update();
    }

    public TabControlView(Context context, AttrSet attrs) throws Exception {
        super(context, attrs);

        // Initialize
        init(context);
        initAttrs(context, attrs);

        // Setup the view
        update();
    }

    private void initAttrs(Context context, AttrSet attrs) throws Exception {
        mTextSize = TypedAttrUtils.getDimensionPixelSize(attrs, "tcv_textSize",
            DisplayUtils.vp2px(context, mDefaultTextSize));
        mSelectedColor = TypedAttrUtils.getIntColor(attrs,
            "tcv_selectedColor", Color.getIntColor("#299EE3"));
        mUnselectedColor = TypedAttrUtils.getIntColor(attrs,
            "tcv_unselectedColor", Color.TRANSPARENT.getValue());
        mSelectedTextColor = TypedAttrUtils.getIntColor(attrs,
            "tcv_selectedTextColor", Color.WHITE.getValue());
        mUnselectedTextColor = TypedAttrUtils.getIntColor(attrs,
            "tcv_unselectedTextColor", Color.getIntColor("#299EE3"));
        mStrokeWidth = TypedAttrUtils.getDimensionPixelSize(attrs,
            "tcv_strokeWidth", DisplayUtils.vp2px(context, 1));
        mItemPadding = TypedAttrUtils.getDimensionPixelSize(attrs,
            "tcv_item_padding", mDefaultItemPadding);
        mItemPaddingHorizontal = TypedAttrUtils.getDimensionPixelSize(attrs,
            "tcv_item_padding_horizontal", mDefaultItemPadding);
        mItemPaddingVertical = TypedAttrUtils.getDimensionPixelSize(attrs,
            "tcv_item_padding_vertical", mDefaultItemPadding);

        // Set text mSelectedColor state list
        mTextColorStateList = new StateElement();
        ShapeElement selectElement = new ShapeElement();
        selectElement.setRgbColor(new RgbColor(mSelectedTextColor));
        mTextColorStateList.addState(new int[]{ComponentState.COMPONENT_STATE_CHECKED}, selectElement);
        ShapeElement unSelectElement = new ShapeElement();
        unSelectElement.setRgbColor(new RgbColor(mUnselectedTextColor));
        mTextColorStateList.addState(new int[]{ComponentState.COMPONENT_STATE_EMPTY}, unSelectElement);

        mDefaultSelection = AttrValue.get(attrs, "tcv_defaultSelection", mDefaultSelection);
        mEqualWidth = AttrValue.get(attrs, "tcv_equalWidth", mEqualWidth);
        mStretch = AttrValue.get(attrs, "tcv_stretch", mStretch);

        String[] itemArray = context.getStringArray(ResourceTable.Strarray_default_array);
        String[] valueArray = context.getStringArray(ResourceTable.Strarray_default_array);

        // Item and value arrays need to be of the same length
        setItems(itemArray, valueArray);
    }

    private void init(Context context) {
        mContext = context;

        setPadding(mPadding, mPadding, mPadding, mPadding);
    }

    /**
     * 更新显示
     */
    private void update() {
        // Remove all views...
        removeAllComponents();

        // Ensure orientation is horizontal
        setOrientation(RadioContainer.HORIZONTAL);

        float textWidth = 0;
        mOptions = new ArrayList<>();

        Iterator<Map.Entry<String, String>> itemIterator = mItemMap.entrySet().iterator();
        int i = 0;
        while (itemIterator.hasNext()) {
            RadioButton rb = new RadioButton(mContext);
            rb.setButtonElement(null);
            rb.setTextColorOn(new Color(mSelectedTextColor));
            rb.setTextColorOff(new Color(mUnselectedTextColor));
            LayoutConfig params = new LayoutConfig(ComponentContainer.LayoutConfig.MATCH_CONTENT,
                ComponentContainer.LayoutConfig.MATCH_PARENT);
            if (mStretch) {
                params.weight = 1.0F;
            }
            if (i > 0) {
                params.setMarginLeft(-mStrokeWidth);
            }

            rb.setLayoutConfig(params);

            // Clear out button drawable (text only)
            rb.setButtonElement(null);

            // Create state list for background
            ShapeElement unSelectedElement;
            ShapeElement selectedElement;
            int radius = DisplayUtils.vp2px(getContext(), mDefaultRadius);
            if (i == 0) {
                // Left
                if (isRtl()) {
                    unSelectedElement = new ShapeElement(getContext(), ResourceTable.Graphic_tcv_right_option);
                    unSelectedElement.setCornerRadiiArray(new float[]{
                        0, 0,
                        radius, radius,
                        radius, radius,
                        0, 0,
                    });
                    selectedElement = new ShapeElement(getContext(), ResourceTable.Graphic_tcv_right_option_selected);
                    selectedElement.setCornerRadiiArray(new float[]{
                        0, 0,
                        radius, radius,
                        radius, radius,
                        0, 0,
                    });
                } else {
                    unSelectedElement = new ShapeElement(getContext(), ResourceTable.Graphic_tcv_left_option);
                    unSelectedElement.setCornerRadiiArray(new float[]{
                        radius, radius,
                        0, 0,
                        0, 0,
                        radius, radius,
                    });
                    selectedElement = new ShapeElement(getContext(), ResourceTable.Graphic_tcv_left_option_selected);
                    selectedElement.setCornerRadiiArray(new float[]{
                        radius, radius,
                        0, 0,
                        0, 0,
                        radius, radius,
                    });
                }
                updateRadioButton(rb, unSelectedElement, selectedElement);
            } else if (i == (mItemMap.size() - 1)) {
                // Right
                if (isRtl()) {
                    unSelectedElement = new ShapeElement(getContext(), ResourceTable.Graphic_tcv_left_option);
                    unSelectedElement.setCornerRadiiArray(new float[]{
                        radius, radius,
                        0, 0,
                        0, 0,
                        radius, radius,
                    });
                    selectedElement = new ShapeElement(getContext(), ResourceTable.Graphic_tcv_left_option_selected);
                    selectedElement.setCornerRadiiArray(new float[]{
                        radius, radius,
                        0, 0,
                        0, 0,
                        radius, radius,
                    });
                } else {
                    unSelectedElement = new ShapeElement(getContext(), ResourceTable.Graphic_tcv_right_option);
                    unSelectedElement.setCornerRadiiArray(new float[]{
                        0, 0,
                        radius, radius,
                        radius, radius,
                        0, 0,
                    });
                    selectedElement = new ShapeElement(getContext(), ResourceTable.Graphic_tcv_right_option_selected);
                    selectedElement.setCornerRadiiArray(new float[]{
                        0, 0,
                        radius, radius,
                        radius, radius,
                        0, 0,
                    });
                }
                updateRadioButton(rb, unSelectedElement, selectedElement);
            } else {
                // Middle
                unSelectedElement = new ShapeElement(getContext(), ResourceTable.Graphic_tcv_middle_option);
                selectedElement = new ShapeElement(getContext(), ResourceTable.Graphic_tcv_middle_option_selected);
                updateRadioButton(rb, unSelectedElement, selectedElement);
            }

            rb.setLayoutConfig(params);
            if (mItemPadding != mDefaultItemPadding) {
                int vpPadding = DisplayUtils.px2vp(getContext(), mItemPadding);
                rb.setPadding(vpPadding, vpPadding, vpPadding, vpPadding);
            }
            if (mItemPaddingHorizontal != mDefaultItemPadding && mItemPaddingVertical != mDefaultItemPadding) {
                rb.setPadding(mItemPaddingHorizontal, mItemPaddingVertical,
                    mItemPaddingHorizontal, mItemPaddingVertical);
            }
            rb.setMinWidth(DisplayUtils.px2vp(getContext(), mStrokeWidth) * VALUE_TEN);
            rb.setTextSize(mTextSize);
            rb.setTextAlignment(TextAlignment.CENTER);
            Map.Entry<String, String> item = itemIterator.next();
            rb.setText(item.getKey());
            textWidth = Math.max(rb.getMaxTextWidth(), textWidth);
            mOptions.add(rb);

            i++;
        }

        // We do this to make all the segments the same width
        for (RadioButton option : mOptions) {
            option.setCheckedStateChangedListener(mCheckedStateChangedListener);
            if (mEqualWidth) {
                option.setWidth((int) (textWidth + (DisplayUtils.px2vp(getContext(), mStrokeWidth) * VALUE_TWENTY)));
            }
            addComponent(option);
        }

        this.setMarkChangedListener(mSelectionChangedListener);

        if (mDefaultSelection > DEFAULT_SELECT) {
            RadioButton radioButton = (RadioButton) getComponentAt(mDefaultSelection);
            if (radioButton != null) {
                this.mark(mDefaultSelection);
                radioButton.setChecked(true);
            }
        }
    }

    public boolean isRtl() {
        return getLayoutDirection() == LayoutDirection.RTL;
    }

    /**
     * 更新RadioButton的显示
     *
     * @param rb radioButton
     * @param unselected 未选中样式
     * @param selected 选中样式
     */
    private void updateRadioButton(RadioButton rb, ShapeElement unselected, ShapeElement selected) {
        rb.setPadding(mPadding, mPaddingTopBottom, mPadding, mPaddingTopBottom);
        unselected.setStroke(mStrokeWidth, new RgbColor(RgbColor.fromArgbInt(mSelectedColor)));
        unselected.setRgbColor(new RgbColor(RgbColor.fromArgbInt(mUnselectedColor)));
        selected.setRgbColor(new RgbColor(RgbColor.fromArgbInt(mSelectedColor)));
        selected.setStroke(mStrokeWidth, new RgbColor(RgbColor.fromArgbInt(mSelectedColor)));
        setRadioButtonBackground(rb, unselected, selected);
    }

    /**
     * 设置RadioButton的背景
     *
     * @param rb
     * @param unselected
     * @param selected
     */
    private void setRadioButtonBackground(RadioButton rb, ShapeElement unselected, ShapeElement selected) {
        StateElement stateListDrawable = new StateElement();
        stateListDrawable.addState(new int[]{ComponentState.COMPONENT_STATE_CHECKED}, selected);
        stateListDrawable.addState(new int[]{ComponentState.COMPONENT_STATE_EMPTY}, unselected);
        rb.setBackground(stateListDrawable);
    }

    /**
     * Get currently selected segment and the view mIdentifier
     *
     * @return string array of mIdentifier [0] value of currently selected segment [1]
     */
    public String[] getCheckedWithIdentifier() {
        String identifier = ((RadioButton) getComponentAt(getMarkedButtonId())).getText();
        String value = mItemMap.get(identifier);
        return new String[]{identifier, value};
    }

    /**
     * Get currently selected segment
     *
     * @return value of currently selected segment
     */
    public String getChecked() {
        return mItemMap.get(((RadioButton) getComponentAt(getMarkedButtonId())).getText());
    }

    /**
     * 为每一个选项设置 items and values
     *
     * @param itemArray itemArray
     * @param valueArray valueArray
     * @return return
     * @throws Exception Exception
     */
    public TabControlView setItems(String[] itemArray, String[] valueArray) throws Exception {
        mItemMap.clear();
        if (itemArray != null && valueArray != null) {
            if (itemArray.length != valueArray.length) {
                throw new Exception("Item labels and value arrays must be the same size");
            }
        }
        if (itemArray != null) {
            if (valueArray != null) {
                for (int i = 0; i < itemArray.length; i++) {
                    mItemMap.put(itemArray[i], valueArray[i]);
                }
            } else {
                for (CharSequence item : itemArray) {
                    mItemMap.put(item.toString(), item.toString());
                }
            }
        }
        update();
        return this;
    }

    /**
     * 为每一个选项设置 items and values
     *
     * @param itemArray itemArray
     * @param valueArray valueArray
     * @throws Exception Exception
     */
    protected void setItems(CharSequence[] itemArray, CharSequence[] valueArray) throws Exception {
        if (itemArray != null && valueArray != null) {
            if (itemArray.length != valueArray.length) {
                throw new Exception("Item labels and value arrays must be the same size");
            }
        }
        if (itemArray != null) {
            if (valueArray != null) {
                for (int i = 0; i < itemArray.length; i++) {
                    mItemMap.put(itemArray[i].toString(), valueArray[i].toString());
                }
            } else {
                for (CharSequence item : itemArray) {
                    mItemMap.put(item.toString(), item.toString());
                }
            }
        }
    }

    /**
     * 为每一个选项设置 items and values .并且设置一个默认选中的序号
     *
     * @param items items
     * @param values values
     * @param defaultSelection defaultSelection
     * @return return
     * @throws Exception Exception
     */
    public TabControlView setItems(String[] items, String[] values, int defaultSelection) throws Exception {
        if (defaultSelection > (items.length - 1)) {
            throw new Exception("Default selection cannot be greater than the number of items");
        } else {
            mDefaultSelection = defaultSelection;
            setItems(items, values);
        }
        return this;
    }

    /**
     * 设置默认选中的Tab
     *
     * @param defaultSelection defaultSelection
     * @return return
     * @throws Exception Exception
     */
    public TabControlView setDefaultSelection(int defaultSelection) throws Exception {
        if (defaultSelection > (mItemMap.size() - 1)) {
            throw new Exception("Default selection cannot be greater than the number of items");
        } else {
            mDefaultSelection = defaultSelection;
            update();
        }
        return this;
    }

    /**
     * 通过值 设置选中的Tab
     *
     * @param value value
     * @return return
     */
    public TabControlView setSelection(String value) {
        setSelection(value, true);
        return this;
    }

    /**
     * 通过值 设置选中的Tab
     *
     * @param value value
     * @param isSilent isSilent
     * @return return
     */
    public TabControlView setSelection(String value, boolean isSilent) {
        String title = getTitleByValue(value);
        setSelectionTitle(title, isSilent);
        return this;
    }

    /**
     * 通过标题设置选中的Tab
     *
     * @param title title
     * @return return
     */
    public TabControlView setSelectionTitle(String title) {
        setSelectionTitle(title, true);
        return this;
    }

    /**
     * 通过标题设置选中的Tab
     *
     * @param title title
     * @param isSilent 是否静默设置
     * @return return
     */
    public TabControlView setSelectionTitle(String title, boolean isSilent) {
        for (RadioButton option : mOptions) {
            if (option.getText().equalsIgnoreCase(title)) {
                if (isSilent) {
                    setMarkChangedListener(null);
                    this.mark(option.getId());
                    setMarkChangedListener(mSelectionChangedListener);
                } else {
                    this.mark(option.getId());
                }
            }
        }
        return this;
    }

    private String getTitleByValue(String value) {
        if (mItemMap.containsValue(value)) {
            String title;
            for (String key : mItemMap.keySet()) {
                title = mItemMap.get(key);
                if (title != null && title.equalsIgnoreCase(value)) {
                    return key;
                }
            }
        }
        return "";
    }

    /**
     * Sets the colors used when drawing the view. The primary color will be used for selected color
     * and unselected text color, while the secondary color will be used for unselected color
     * and selected text color.
     *
     * @param primaryColor primaryColor
     * @param secondaryColor secondaryColor
     * @return return
     */
    public TabControlView setColors(int primaryColor, int secondaryColor) {
        mSelectedColor = primaryColor;
        mSelectedTextColor = secondaryColor;
        mUnselectedColor = secondaryColor;
        mUnselectedTextColor = primaryColor;

        // Set text mSelectedColor state list
        mTextColorStateList = new StateElement();
        ShapeElement selectElement = new ShapeElement();
        selectElement.setRgbColor(new RgbColor(mSelectedTextColor));
        mTextColorStateList.addState(new int[]{ComponentState.COMPONENT_STATE_CHECKED}, selectElement);
        ShapeElement unSelectElement = new ShapeElement();
        unSelectElement.setRgbColor(new RgbColor(mUnselectedTextColor));
        mTextColorStateList.addState(new int[]{ComponentState.COMPONENT_STATE_EMPTY}, unSelectElement);

        update();
        return this;
    }

    /**
     * 设置颜色
     *
     * @param selectedColor selectedColor
     * @param selectedTextColor selectedTextColor
     * @param unselectedColor unselectedColor
     * @param unselectedTextColor unselectedTextColor
     * @return return
     */
    public TabControlView setColors(int selectedColor, int selectedTextColor,
                                    int unselectedColor, int unselectedTextColor) {
        mSelectedColor = selectedColor;
        mSelectedTextColor = selectedTextColor;
        mUnselectedColor = unselectedColor;
        mUnselectedTextColor = unselectedTextColor;

        // Set text mSelectedColor state list
        mTextColorStateList = new StateElement();
        ShapeElement selectElement = new ShapeElement();
        selectElement.setRgbColor(new RgbColor(selectedTextColor));
        mTextColorStateList.addState(new int[]{ComponentState.COMPONENT_STATE_CHECKED}, selectElement);
        ShapeElement unSelectElement = new ShapeElement();
        unSelectElement.setRgbColor(new RgbColor(unselectedTextColor));
        mTextColorStateList.addState(new int[]{ComponentState.COMPONENT_STATE_EMPTY}, unSelectElement);

        update();
        return this;
    }

    /**
     * 设置Tab选中的监听
     *
     * @param listener listener
     * @return return
     */
    public TabControlView setOnTabSelectionChangedListener(OnTabSelectionChangedListener listener) {
        mListener = listener;
        return this;
    }

    /**
     * 设置是否等宽
     *
     * @param isEqualWidth isEqualWidth
     * @return return
     */
    public TabControlView setEqualWidth(boolean isEqualWidth) {
        mEqualWidth = isEqualWidth;
        update();
        return this;
    }

    /**
     * 设置是否伸展开（match_parent)
     *
     * @param isStretch isStretch
     * @return return
     */
    public TabControlView setStretch(boolean isStretch) {
        mStretch = isStretch;
        update();
        return this;
    }

    @Override
    public void setTypeface(Font typeface) {
        if (mOptions != null) {
            for (int i = 0; i < mOptions.size(); i++) {
                mOptions.get(i).setFont(typeface);
            }
        }
    }

    @Override
    public void onAttributeConfigChanged(Configuration newConfig) {
        super.onAttributeConfigChanged(newConfig);
        update();
    }

    /**
     * Tab选中的监听
     */
    public interface OnTabSelectionChangedListener {
        /**
         * 选中
         *
         * @param title 选中展示的内容
         * @param value 选中的值
         */
        void newSelection(String title, String value);
    }
}
